In [2]:
import ipywidgets as widgets
import numpy as np
import matplotlib.pyplot as plt
import skrf as rf
rf.stylely()
%matplotlib notebook
For simplicity we look to a single frequency
In [3]:
frq = rf.Frequency(55,55,1,'mhz')
We now crate a network object for each components
In [5]:
hybA = rf.Network(name = 'hybrid_A')
hybB = rf.Network(name = 'hybrid_B')
phsC = rf.Network(name = 'phase_shift_coupl')
hybA.frequency = frq
hybB.frequency = frq
phsC.frequency = frq
We define the S matrix of the hybrid coupler
In [6]:
_shyb = np.array([[[0, -1j/np.sqrt(2), -1/np.sqrt(2), 0],
[-1j/np.sqrt(2), 0, 0, -1/np.sqrt(2)],
[-1/np.sqrt(2), 0, 0, -1j/np.sqrt(2)],
[0, -1/np.sqrt(2), -1j/np.sqrt(2), 0]]])
We define a function that returns the S matrix for thepahse shifter
In [7]:
def phase_shifter(phase):
_sph = np.array([[0,np.exp(-1j*phase)],[np.exp(-1j*phase),0]])
return _sph
In case we have more frequency points, we replicate the S matrix of the hybrids for each frequency point. The result is a network with the same ideal response for each frequency.
In [8]:
hybA.s = np.repeat(_shyb, frq.npoints, axis=0)
hybB.s = np.repeat(_shyb, frq.npoints, axis=0)
We create the ports for the circuit network
In [10]:
port1 = rf.Circuit.Port(frq, 'port1', z0=50.)
port2 = rf.Circuit.Port(frq, 'port2', z0=50.)
port3 = rf.Circuit.Port(frq, 'port3', z0=50.)
port4 = rf.Circuit.Port(frq, 'port4', z0=50.)
We create replicate the S matrix for the phase shifter for a given phase, e.g. 90 deg.
In [32]:
phsC.s = np.tile(phase_shifter(np.pi/2), frq.npoints)
We define the connections for the circuit
In [12]:
cnx = [
[(port1, 0), (hybA, 0)],
[(port2, 0), (hybB, 1)],
[(port3, 0), (hybB, 2)],
[(port4, 0), (hybA, 3)],
[(hybA, 1), (hybB, 0)],
[(hybA, 2), (phsC, 0)],
[(phsC, 1), (hybB, 3)],
]
We create the circuit and we check what is the amplitude at the two output for a given input on port 1
In [13]:
circuit = rf.Circuit(cnx)
out = circuit.network.s[0,:,:] @ np.array([1,0,0,0]).T
print(f'Output signal amplitude: {np.abs(out)}')
We now define some auxiliary funcitons to make the plots
In [14]:
def sig(a,p):
return a*np.exp(-1j*p)
In [15]:
def prep_signal(s):
r = np.abs(s)
ph = np.angle(s)
per = np.linspace(0,2*np.pi)
return (per/np.pi,r*np.sin(per+ph))
In [16]:
def cv(v):
return np.array([v]).T
In [17]:
def calc(p1,p4,phase):
_, pA2u, pA3u, _ = _shyb.squeeze() @ cv([p1,0,0,0])
_, pS2u = phase_shifter(phase) @ cv([pA3u.tolist()[0],0])
_, pB2u, pB3u, _ = _shyb.squeeze() @ cv([pA2u.tolist()[0],0,0,pS2u.tolist()[0]])
_, pA2d, pA3d, _ = _shyb.squeeze() @ cv([0,0,0,p4])
_, pS2d = phase_shifter(phase) @ cv([pA3d.tolist()[0],0])
_, pB2d, pB3d, _ = _shyb.squeeze() @ cv([pA2d.tolist()[0],0,0,pS2d.tolist()[0]])
_, pA2, pA3, _ = _shyb.squeeze() @ cv([p1,0,0,p4])
_, pS2 = phase_shifter(phase) @ cv([pA3.tolist()[0],0])
_, pB2, pB3, _ = _shyb.squeeze() @ cv([pA2.tolist()[0],0,0,pS2.tolist()[0]])
return (pA2u,pA3u,pS2u,pB2u,pB3u,
pA2d,pA3d,pS2d,pB2d,pB3d,
pA2,pA3,pS2,pB2,pB3)
We create 2 signals for the two input ports (#1, #4)
In [18]:
p1 = sig(2,0)
p4 = sig(2,0)
phase = -np.pi/2
pA2u,pA3u,pS2u,pB2u,pB3u,pA2d,pA3d,pS2d,pB2d,pB3d,pA2,pA3,pS2,pB2,pB3 = calc(p1,p4,phase)
We can plot the signals on each port of the hybrids to visualize the power flow of the variable coupler
In [31]:
fig = plt.figure(figsize=(7,3))
lm = np.round(np.max(np.abs([pB2,pB3])))
ax1 = fig.add_subplot(2,4,1)
line11, = ax1.plot(*prep_signal(p1),'r')
plt.ylim(-lm,lm)
plt.xlabel('port 1')
ax2 = fig.add_subplot(2,4,2)
line21, = ax2.plot(*prep_signal(pA2u),'r--')
line22, = ax2.plot(*prep_signal(pA2d),'b--')
line23, = ax2.plot(*prep_signal(pA2),'m')
plt.ylim(-lm,lm)
plt.xlabel('pA2')
ax3 = fig.add_subplot(2,4,3)
line31, = ax3.plot(*prep_signal(pA2u),'w--')
line32, = ax3.plot(*prep_signal(pA2d),'w--')
line33, = ax3.plot(*prep_signal(pA2),'m')
plt.ylim(-lm,lm)
plt.xlabel('pA2')
ax4 = fig.add_subplot(2,4,4)
line41, = ax4.plot(*prep_signal(pB2u),'m--')
line42, = ax4.plot(*prep_signal(pB2d),'c--')
line43, = ax4.plot(*prep_signal(pB2))
plt.ylim(-lm,lm)
plt.xlabel('port 2')
ax5 = fig.add_subplot(2,4,5)
line51, = ax5.plot(*prep_signal(p4),'b')
plt.ylim(-lm,lm)
plt.xlabel('port 4')
ax6 = fig.add_subplot(2,4,6)
line61, = ax6.plot(*prep_signal(pA3u),'r--')
line62, = ax6.plot(*prep_signal(pA3d),'b--')
line63, = ax6.plot(*prep_signal(pA3))
plt.ylim(-lm,lm)
plt.xlabel('pA3')
ax7 = fig.add_subplot(2,4,7)
line71, = ax7.plot(*prep_signal(pS2u),'w--')
line72, = ax7.plot(*prep_signal(pS2d),'w--')
line73, = ax7.plot(*prep_signal(pS2),'c')
plt.ylim(-lm,lm)
plt.xlabel('pS2')
ax8 = fig.add_subplot(2,4,8)
line81, = ax8.plot(*prep_signal(pB3u),'m--')
line82, = ax8.plot(*prep_signal(pB3d),'c--')
line83, = ax8.plot(*prep_signal(pB3),'g')
plt.ylim(-lm,lm)
plt.xlabel('port 3')
plt.tight_layout()
In [33]:
fig = plt.figure('Response',figsize=(7,3))
#fig.clear()
lm = np.round(np.max(np.abs([pB2,pB3])))
#plot_ax = plt.axes([0.1, 0.1, 0.8, 0.65])
#phase_ax = plt.axes([0.06, 0.02, 0.35, 0.02])
#phase_ax2 = plt.axes([0.06, 0.05, 0.35, 0.02])
#ampli_ax = plt.axes([0.57, 0.05, 0.35, 0.02])
#plt.axes(plot_ax)
#ax1 = fig.add_subplot(2,4,1)
ax1 = fig.add_axes([0.05,0.65,0.18,0.30])
line11, = ax1.plot(*prep_signal(p1),'r')
plt.ylim(-lm,lm)
plt.xlabel('port 1')
#ax2 = fig.add_subplot(2,4,2)
ax2 = fig.add_axes([0.3,0.65,0.18,0.30])
line21, = ax2.plot(*prep_signal(pA2u),'r--')
line22, = ax2.plot(*prep_signal(pA2d),'b--')
line23, = ax2.plot(*prep_signal(pA2),'m')
plt.ylim(-lm,lm)
plt.xlabel('pA2')
#ax3 = fig.add_subplot(2,4,3)
ax3 = fig.add_axes([0.55,0.65,0.18,0.30])
line31, = ax3.plot(*prep_signal(pA2u),'w--')
line32, = ax3.plot(*prep_signal(pA2d),'w--')
line33, = ax3.plot(*prep_signal(pA2),'m')
plt.ylim(-lm,lm)
plt.xlabel('pA2')
#ax4 = fig.add_subplot(2,4,4)
ax4 = fig.add_axes([0.8,0.65,0.18,0.30])
line41, = ax4.plot(*prep_signal(pB2u),'m--')
line42, = ax4.plot(*prep_signal(pB2d),'c--')
line43, = ax4.plot(*prep_signal(pB2))
plt.ylim(-lm,lm)
plt.xlabel('port 2')
#ax5 = fig.add_subplot(2,4,5)
ax5 = fig.add_axes([0.05,0.2,0.18,0.30])
line51, = ax5.plot(*prep_signal(p4),'b')
plt.ylim(-lm,lm)
plt.xlabel('port 4')
#ax6 = fig.add_subplot(2,4,6)
ax6 = fig.add_axes([0.3,0.2,0.18,0.30])
line61, = ax6.plot(*prep_signal(pA3u),'r--')
line62, = ax6.plot(*prep_signal(pA3d),'b--')
line63, = ax6.plot(*prep_signal(pA3))
plt.ylim(-lm,lm)
plt.xlabel('pA3')
#ax7 = fig.add_subplot(2,4,7)
ax7 = fig.add_axes([0.55,0.2,0.18,0.30])
line71, = ax7.plot(*prep_signal(pS2u),'w--')
line72, = ax7.plot(*prep_signal(pS2d),'w--')
line73, = ax7.plot(*prep_signal(pS2),'c')
plt.ylim(-lm,lm)
plt.xlabel('pS2')
#ax8 = fig.add_subplot(2,4,8)
ax8 = fig.add_axes([0.8,0.2,0.18,0.30])
line81, = ax8.plot(*prep_signal(pB3u),'m--')
line82, = ax8.plot(*prep_signal(pB3d),'c--')
line83, = ax8.plot(*prep_signal(pB3),'g')
plt.ylim(-lm,lm)
plt.xlabel('port 3')
#fig.tight_layout()
#plt.title('Test')
@widgets.interact(a=(0,2.),ph=(0,2.),ph2=(0,2.))
def update(ph,ph2,a):
#pA2u,pA3u,pS2u,pB2u,pB3u,pA2d,pA3d,pS2d,pB2d,pB3d,pA2,pA3,pS2,pB2,pB3 = calc(p1,p4,ph)
#0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
p1 = sig(2, 0)
p4 = sig(a, ph2*np.pi)
l = calc(p1,p4,ph*np.pi)
line21.set_ydata(prep_signal(l[0])[1])
line22.set_ydata(prep_signal(l[5])[1])
line23.set_ydata(prep_signal(l[10])[1])
line31.set_ydata(prep_signal(l[0])[1])
line32.set_ydata(prep_signal(l[5])[1])
line33.set_ydata(prep_signal(l[10])[1])
line41.set_ydata(prep_signal(l[3])[1])
line42.set_ydata(prep_signal(l[8])[1])
line43.set_ydata(prep_signal(l[13])[1])
line51.set_ydata(prep_signal(p4)[1])
line61.set_ydata(prep_signal(l[1])[1])
line62.set_ydata(prep_signal(l[6])[1])
line63.set_ydata(prep_signal(l[11])[1])
line71.set_ydata(prep_signal(l[2])[1])
line72.set_ydata(prep_signal(l[7])[1])
line73.set_ydata(prep_signal(l[12])[1])
line81.set_ydata(prep_signal(l[4])[1])
line82.set_ydata(prep_signal(l[9])[1])
line83.set_ydata(prep_signal(l[14])[1])
fig.canvas.draw_idle()
In [ ]: